//
//  main.m
//  C08_内存管理
//
//  Created by dllo on 15/11/26.
//  Copyright © 2015年 dllo. All rights reserved.
//

#import <Foundation/Foundation.h>

/** 定义两个全局变量 */
int global1 = 100;
int global2 = 200;




void fun(int x, int y){
    
    char string[] = "iphone";
    
    /** 输出参数地址和函数内变量的地址 */
    printf("x: %p\n", &x);
    printf("y: %p\n", &y);
    printf("string: %p\n", string);
   
}

void fun2();

int main(int argc, const char * argv[]) {

#pragma mark - 知识点1 存储区划分
    
#pragma mark ** 栈区
#if 0
    int a = 100;
    int b = 200;
    printf("栈区地址如下:\n");
    printf("a: %p\n", &a);
    printf("b: %p\n", &b);

    /** 结论:栈区是从高到低分配内存 */
    
    /** 查看函数参数内存分配问题 */
    
    fun(a, b);
    
    /** 注意:当调用函数时, 函数的形参先进栈区, 然后是函数内的变量进栈区. 函数调用之后(即执行完函数), 出栈的顺序和进栈的顺序相反, 即先释放函数内变量内存,再释放形参内存 */
#endif
#pragma mark ** 堆区
#if 0
    int *a2 = malloc(4);
    int *b2 = malloc(4);
    printf("堆区地址如下:\n");
    printf("a2 : %p\n", a2);
    printf("b2 : %p\n", b2);
    
    free(a2);  /**< 内存释放 */
    free(b2);  /**< 内存释放 */
#endif
    /** 结论: 堆内存是从低向高分配. 堆内存更多内容, 详见下面知识点2 */
   
#pragma mark ** 全局(静态)区
#if 0
    printf("全局区地址如下:\n");
    printf("global1 : %p\n", &global1);
    printf("global2 : %p\n", &global2);
    
    /** 定义两个静态变量 */
    static int s1 = 100;
    static int s2 = 200;
    
    printf("静态区地址如下:\n");
    printf("s1 : %p\n", &s1);
    printf("s2 : %p\n", &s2);
    
#endif
    /** 结论: 全局(静态)区, 内存分配从低到高 */
    
    
#pragma mark ** 常量区
#if 0
    char *string = "iphone";
    char *string2 = "iOS";
    printf("常量区地址如下:\n");
    printf("string : %p\n", "iphone");
    printf("string : %p\n", string);
    printf("string2 : %p\n", string2);
#endif
    
#if 0  /** 常量不能更改问题 */
    {
        char *str = "iphone";
        printf("%c\n", *(str + 1));  /** 通过指针可以任意读取常量值 */
 //       *(str + 1) = 'P';   /**< 但是常量值不能更改, 报指针错误 */
   
    }
#endif
    
#if 0  /** 容易混淆的地方 */
    {
        char *str1 = "iphone";  /**< 定义一个指针, 指向了常量区地址 */
        
        char str2[] = "iphone";  /**< 定义了一个数组, 将常量区内容复制到栈区 */
        str2[1] = 'P';
    }
#endif
#if 0  /** 地址模糊不清的地方 */
    {
        char *str1 = "iphone";
        printf("address1: %p\n", str1);  /**< 指针指向的地址 */
        printf("address2: %p\n", &str1);  /**< 指针变量本身在栈区的地址 */
    }
#endif
    
#pragma mark ** 代码区
#if 0
    /** 函数代码通常在代码区 */
    printf("代码区地址如下: \n");
    printf("fun : %p\n", fun);
    printf("main: %p\n", main);
    printf("fun2: %p\n", fun2);
    
    /** 结论: 代码区从低向高分配内存 */
#endif
#pragma mark - 知识点2 堆内存分配函数
#if 0
    /** 通过函数 malloc 在堆区开辟空间 */
    int *p = malloc(8);
    *p = 100;     /**< 前4个字节赋值 */
    *(p + 1) = 200;  /**< 后4个字节赋值 */
    
    printf("%d \n", *p);  /**< 访问前4个字节的内容 */
    printf("%d \n", *(p + 1));  /**< 访问后4个字节的内容 */
    printf("%d \n", *(p + 2));  /**< 访问正常开辟的8个字节以外的内容, 但是不报错. 注意: 不能这样写 */
    
    /** 通过 free 函数释放内存 */
    free(p);
    printf("%d \n", *p);   /**< 此时发现,值还是之前的100. 结论: free函数不清空内存 */
    printf("%d\n", *(p + 1));
    
    /** 针对free函数不清空内存问题, 为了不引起类似野指针问题, 通常将指针指向空 */
    p = NULL;
    
    int *pArray = malloc(sizeof(int) * 5);
    *pArray = 3;
    *(pArray + 1) = 2;
    *(pArray + 2) = 6;
    *(pArray + 3) = 4;
    *(pArray + 4) = 7;
    
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(pArray + i));
    }
    printf("\n");
    
    /** 对数组进行排序(冒泡排序) */
    int temp = 0;
    for (int i = 0; i < 5 - 1; i++) {
        for (int j = 0; j < 5 - i - 1; j++) {
            if (*(pArray + j) > *(pArray + j + 1)) {
                temp = *(pArray + j);
                *(pArray + j) = *(pArray + j + 1);
                *(pArray + j + 1) = temp;
            }
        }
    }
    for (int i = 0; i < 5; i++) {
        printf("%d ", *(pArray + i));
    }
    printf("\n");
    free(pArray);
    pArray = NULL;
#endif
#if 0
    /** 有一已知字符串，其中包含 数字 字符，提取其中的 数字字符 ，要求动态分配内存保存。
     提示: 先计算出有几个数字字符，然后根据数字字符的个数在堆区开辟空间 */
    char str[] = "fwe44gfg32";
    char *p =str;
    int i = 0;  
    int count = 0;
    
    while (*(p + i) != '\0' ) {
        if (*(p + i) >= '0' && *(p + i) <= '9') {
            count++;
        }
        i++;
    }
    
    /** 堆区开辟空间 */
    char *p1 = malloc(sizeof(char) * count + 1);  /** 多一个字节 存放\0 */
    i = 0;
    int j = 0;
    while (*(p + i) != '\0'){
        if (*(p + i) >= '0' && *(p + i) <= '9') {
            *(p1 + j) = *(p + i);
            j++;
        }
        i++;
    }
    
    *(p1 + j) = '\0';  /** 最后一个字节赋值\0 */
    printf("%s\n", p1);
#endif
    /** 输入3个单词，动态分配内存保存单词，并在最后输出。
     提示:定义一个指针数组 char * words[3] = {0}； 保存堆区空间的地址，堆区空间存储数据。 */
#if 0
    char *words[3] = {0};
    char temp[50] = {};
    
    for (int i = 0; i < 3; i++) {
    printf("请输入单词:");
    rewind(stdin);  /** 清空输入缓存流 */
    scanf("%s", temp);
    
    /** 堆区开辟空间 */
    
    char *str = malloc((strlen(temp) + 1) * sizeof(char));
    strcpy(str, temp);   /**< 将temp字符串拷贝到str字符串中 */
    words[i] = str;  /**< 将堆内存指针存入指针数组中 */
      
    }
    
    /** 遍历指针数组, 输出每个单词 */
    for (int i = 0; i < 3; i++) {
        printf("%s \n", words[i]);
    }
    /** 堆内存释放 */
    for (int i = 0; i < 3; i++) {
        free(words[i]);
        words[i] = NULL;
    }
#endif
    
#pragma mark - 知识点3 其他内存分配函数
    
    /** calloc 函数, 分配内存,并且清零 */
    char *p = calloc(4, 5);  /**< 注意: 是4个5字节 */
    
    free(p);
    
    /** realloc 函数, 对之前分配的内存空间,重新调整新的大小 */
    
    char *p2 = malloc(10);
    char *newP = realloc(p2, 30);
    free(newP);  /**< 注意: 之前的p2是否要释放掉呢? 详见 API */
    
    
    
   
    return 0;
}

void fun2(){
    printf("%p \n", fun2);
    
}






